package main

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"text/template"
)

var coderModes = []string{"mybatis-plus(nl)+vue",
	"mybatis-plus+vue",
	"mybatis-plus+vue(antd-vue)",
	"mybatis-plus",
	"mybatis",
	"xorm+vue",
	"xorm+vue(antd-vue)",
}

const TemplateName = "TemplateName"

// 下划线转首字母小写的驼峰
func formatSmallCamel(str string) string {
	data := make([]byte, 0, len(str))
	j := false
	k := true
	num := len(str) - 1
	for i := 0; i <= num; i++ {
		d := str[i]
		if k == false && d >= 'A' && d <= 'Z' {
			k = true
		}
		if d >= 'a' && d <= 'z' && (j || k == false) {
			d = d - 32
			j = false
			k = true
		}
		if k && d == '_' && num > i && str[i+1] >= 'a' && str[i+1] <= 'z' {
			j = true
			continue
		}
		data = append(data, d)
	}
	return string(data[:])
}

// 下划线转首字母大写的驼峰
func formatBigCamel(str string) string {
	str = strings.Replace(str, "_", " ", -1)
	str = strings.Title(str)
	return strings.Replace(str, " ", "", -1)
}

// 数据库类型和Java的Mybatis框架类型互相转换
func formatJavaDataType(str string) string {
	str = strings.ToLower(str)
	types := map[string]string{
		"int":        "Integer",
		"tinyint":    "Byte",
		"smallint":   "Short",
		"mediumint":  "Integer",
		"bigint":     "Long",
		"bit":        "Boolean",
		"double":     "Double",
		"float":      "Float",
		"number":     "java.math.BigDecimal",
		"decimal":    "java.math.BigDecimal",
		"char":       "String",
		"varchar":    "String",
		"date":       "java.util.Date",
		"time":       "java.util.Date",
		"year":       "java.util.Date",
		"timestamp":  "java.util.Date",
		"datetime":   "java.util.Date",
		"tinyblob":   "Byte[]",
		"blob":       "Byte[]",
		"mediumblob": "Byte[]",
		"longblob":   "Byte[]",
		"tinytext":   "String",
		"text":       "String",
		"mediumtext": "String",
		"longtext":   "String",
		"binary":     "Byte[]",
		"varbinary":  "Byte[]",
	}
	return types[str]
}

// 生成实体类需要排除的字段
func formatEntityField(fieldName string) bool {
	if strings.Contains(fieldName, "create_id") ||
		strings.Contains(fieldName, "create_time") ||
		strings.Contains(fieldName, "update_id") ||
		strings.Contains(fieldName, "update_time") ||
		strings.Contains(fieldName, "del_flag") ||
		strings.Contains(fieldName, "create_time") ||
		strings.Contains(fieldName, "creator_id") ||
		strings.Contains(fieldName, "latest_update_time") ||
		strings.Contains(fieldName, "latest_updater_id") {
		return false
	}
	return true
}

func (f *MainForm) runCoder(dbIndex, modeIndex int32, tables, basePath, basePackage string, callback func(log string)) {
	tableList := strings.Split(tables, separator)
	for _, tableName := range tableList {
		if len(tableName) == 0 {
			continue
		}
		fields := loadFields(tableName, DBConfig{
			DBHost:     dbConfigs[dbIndex].DBHost,
			DBName:     dbConfigs[dbIndex].DBName,
			DBUsername: dbConfigs[dbIndex].DBUsername,
			DBPassword: dbConfigs[dbIndex].DBPassword,
		})
		bigCamelTableName := formatBigCamel(tableName)
		filePath := filepath.Join(basePath, tableName)
		if modeIndex == 0 {
			data := map[string]interface{}{
				"BasePackageName": defaultBasePackage,
				"PackageName":     tableName,
				"TableName":       tableName,
				"Fields":          fields,
				"FieldIdName":     fieldId(fields),
				"TableFields":     fields,
				"FormFields":      fields,
				"FormFieldLength": len(fields),
				"RuleFields":      fields,
				"RuleFieldLength": len(fields),
			}
			runTpl(filepath.Join(filePath, "controller", bigCamelTableName+"Controller.java"), nlController, data)
			callback(fmt.Sprintf("已生成 %s 表，controller数据", tableName))
			runTpl(filepath.Join(filePath, "service", bigCamelTableName+"Service.java"), nlService, data)
			callback(fmt.Sprintf("已生成 %s 表，service数据", tableName))
			runTpl(filepath.Join(filePath, "entity", bigCamelTableName+".java"), nlEntity, data)
			callback(fmt.Sprintf("已生成 %s 表，entity数据", tableName))
			runTpl(filepath.Join(filePath, "mapper", bigCamelTableName+"Mapper.java"), nlMapper, data)
			callback(fmt.Sprintf("已生成 %s 表，mapper数据", tableName))
			runTpl(filepath.Join(filePath, "mapper", "xml", bigCamelTableName+"Mapper.xml"), nlMapperXml, data)
			callback(fmt.Sprintf("已生成 %s 表，mapper xml数据", tableName))
			runTpl(filepath.Join(filePath, "vue", bigCamelTableName+".vue"), nlVue, data)
			callback(fmt.Sprintf("已生成 %s 表，vue数据", tableName))
			runTpl(filepath.Join(filePath, "vue", "api", bigCamelTableName+".js"), nlVueApi, data)
			callback(fmt.Sprintf("已生成 %s 表，vue api数据", tableName))
		} else if modeIndex == 1 {
			data := map[string]interface{}{
				"BasePackageName": defaultBasePackage,
				"PackageName":     tableName,
				"TableName":       tableName,
				"Fields":          fields,
			}
			runTpl(filepath.Join(filePath, "controller", bigCamelTableName+"Controller.java"), commonController, data)
			callback(fmt.Sprintf("已生成 %s 表，controller数据", tableName))
			runTpl(filepath.Join(filePath, "service", bigCamelTableName+"Service.java"), commonService, data)
			callback(fmt.Sprintf("已生成 %s 表，service数据", tableName))
			runTpl(filepath.Join(filePath, "entity", bigCamelTableName+".java"), commonEntity, data)
			callback(fmt.Sprintf("已生成 %s 表，entity数据", tableName))
			runTpl(filepath.Join(filePath, "mapper", bigCamelTableName+"Mapper.java"), commonMapper, data)
			callback(fmt.Sprintf("已生成 %s 表，mapper数据", tableName))
			runTpl(filepath.Join(filePath, "mapper", "xml", bigCamelTableName+"Mapper.xml"), commonMapperXml, data)
			callback(fmt.Sprintf("已生成 %s 表，mapper xml数据", tableName))
			runTpl(filepath.Join(filePath, "vue", bigCamelTableName+".vue"), commonVue, data)
			callback(fmt.Sprintf("已生成 %s 表，vue数据", tableName))
			runTpl(filepath.Join(filePath, "vue", "api", bigCamelTableName+".js"), commonVueApi, data)
			callback(fmt.Sprintf("已生成 %s 表，vue api数据", tableName))
		} else if modeIndex == 3 {
			// 只生成mybatis-plus相关类
			data := map[string]interface{}{
				"BasePackageName": basePackage,
				"PackageName":     tableName,
				"TableName":       tableName,
				"Fields":          fields,
				"HasAuto":         fieldHasAuto(fields),
			}
			runTpl(filepath.Join(filePath, "entity", bigCamelTableName+".java"), mybatisPlusEntity, data)
			callback(fmt.Sprintf("已生成 %s 表，entity数据", tableName))
			runTpl(filepath.Join(filePath, "mapper", bigCamelTableName+"Mapper.java"), mybatisPlusMapper, data)
			callback(fmt.Sprintf("已生成 %s 表，mapper数据", tableName))
			runTpl(filepath.Join(filePath, "mapper", "xml", bigCamelTableName+"Mapper.xml"), mybatisPlusMapperXml, data)
		}
	}
}

func runTpl(filePath string, tplData string, data map[string]interface{}) {
	var (
		err  error
		tmpl *template.Template
		file *os.File
	)
	if err = os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil {
		fmt.Println(err)
	}
	if file, err = os.OpenFile(filePath, os.O_CREATE, os.ModePerm); err != nil {
		fmt.Println(err)
	}
	defer func() {
		_ = file.Close()
	}()
	if tmpl, err = template.New(TemplateName).Funcs(template.FuncMap{
		"formatSmallCamel":   formatSmallCamel,
		"formatBigCamel":     formatBigCamel,
		"formatJavaDataType": formatJavaDataType,
		"formatEntityField":  formatEntityField,
	}).Delims("${", "}").Parse(tplData); err != nil {
		fmt.Println(err)
		return
	}
	if err = tmpl.Execute(file, data); err != nil {
		fmt.Println(err)
	}
}
